new: async package for Solid 2.0#909
Conversation
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
feat: fromJSONStream - new primitive test: improve tests feat: makeRetrying WIP
feat: support node-based web streams
docs: first storybook stories, README finalized
docs: finish stories
docs: finish stories
There was a problem hiding this comment.
Actionable comments posted: 13
🧹 Nitpick comments (1)
packages/async/test/index.test.ts (1)
14-15: ⚡ Quick winClose test streams when data is exhausted.
Both helper streams return on
donewithoutcontroller.close(), so completion semantics (done: true) aren’t being exercised cleanly.Proposed fix
- if (done) return; + if (done) { + controller.close(); + return; + } @@ - if (done) return; + if (done) { + controller.close(); + return; + }Also applies to: 62-63
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/async/test/index.test.ts` around lines 14 - 15, The helper streams in the test are not properly closing when data is exhausted. In both locations where the code checks if done is true and returns early (around the controller.enqueue calls), add a controller.close() call before the return statement to ensure the stream completion semantics are properly exercised. This applies to both instances where this pattern appears in the file.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/async/package.json`:
- Around line 4-5: In the package.json file, update the placeholder metadata
fields: replace the "description" field value from "A template primitive
example." with an accurate description of what the async package does, and
replace the "author" field value from "Your Name <you@youremail.com>" with the
actual author's name and email address. These fields will be published to npm,
so ensure they contain real, meaningful information.
- Around line 35-47: The exports field in package.json declares a
`@solid-primitives/source` export condition that points to ./src/index.ts, but the
files array only includes the dist directory. This creates a mismatch where
consumers trying to use the `@solid-primitives/source` export will fail because
the src directory is not published to npm. Fix this by choosing one of two
approaches: either add "src" to the files array to publish source files
alongside dist, or remove the `@solid-primitives/source` export condition from the
exports field to prevent consumers from attempting to resolve unpublished source
files.
In `@packages/async/README.md`:
- Around line 13-18: The anchor fragments in the README table of contents use
camelCase format (fromStream, fromJSONStream, createAggregated, etc.) but
standard markdown slug generation converts headings to lowercase. Update all the
anchor fragment links in the primitive index to use lowercase versions
(fromstream, fromjsonstream, makeabortable, createabortable, makeretrying,
createaggregated) so they correctly resolve to the actual heading slugs in the
document.
- Around line 64-66: The README documentation contains mismatches with the
actual implementation for the fromJSONStream API. First, correct the function
label from "fromStream" to "fromJSONStream" in the definition block that shows
the actual function signature. Second, update all documentation examples and
option descriptions to use the correct option name "autoAbort" instead of
"noAutoAbort", ensuring the semantics are clearly documented that autoAbort
controls whether streams are automatically aborted (with inverse logic to what
the docs currently show). Apply these corrections to all instances mentioned
including the definition block around line 64-66 and the additional locations at
lines 108-109 and 118.
In `@packages/async/src/index.ts`:
- Around line 229-231: The toArray function uses a falsy check that incorrectly
converts valid falsy values like 0, false, and empty strings into empty arrays,
losing data during aggregation. Replace the ternary condition in toArray to
explicitly check for null or undefined values only (using == null or equivalent
checks) instead of relying on truthiness, so that valid falsy values like 0,
false, and "" are wrapped in arrays rather than discarded. Apply the same fix to
the other instance mentioned at lines 256-257.
- Around line 171-173: The isIterable and isAsyncIterable functions use
Object.hasOwn to check for Symbol.iterator and Symbol.asyncIterator, but this
only detects own properties and misses symbols on the prototype chain. Arrays,
strings, and generators have these symbols inherited from their prototypes,
causing makeRetrying to incorrectly treat them as single values. Replace
Object.hasOwn with the in operator for both isIterable and isAsyncIterable,
since the in operator checks the entire prototype chain and will correctly
identify all standard iterables and async iterables.
- Around line 195-197: The `retries` variable is being declared outside the
generator function and captured in the closure, causing state to leak and be
shared across multiple invocations. Additionally, using the `||` operator with
`options.retries` treats `0` as falsy, preventing `retries: 0` from being a
valid option to disable retries. Move the `retries` variable declaration inside
the generator function so each invocation gets its own independent retry
counter, and change the assignment from `options.retries || 3` to use the
nullish coalescing operator `??` instead to properly handle `0` as a legitimate
value.
In `@packages/async/stories/createAbortable.stories.tsx`:
- Around line 74-76: The input element with placeholder "type for autosuggest"
is using the onChange event handler which only fires on blur in Solid.js,
causing delayed autosuggest updates. Replace the onChange handler with onInput
on this input element to ensure the setQuery callback fires immediately on every
keystroke, providing instant suggestion responsiveness.
In `@packages/async/stories/fromJSONStream.stories.tsx`:
- Line 33: The JSON stream chunking in the anonymous function has an off-by-one
error in the slice end index calculation. The slice call currently subtracts 1
from the end index with `(idx + 1) * sliceLength - 1`, which removes one
character from each chunk and corrupts the JSON parsing. Remove the `- 1` from
the end index so that the slice correctly includes all characters up to the
proper boundary at `(idx + 1) * sliceLength`.
In `@packages/async/stories/fromStream.stories.tsx`:
- Line 33: The slice call in the callback function (around the `source.slice`
expression) is incorrectly subtracting 1 from the end index parameter, which
causes character loss at chunk boundaries because JavaScript's slice method
already treats the end index as exclusive. Remove the `- 1` from the end
parameter calculation in the slice call so it becomes `(idx + 1) * sliceLength`
instead of `(idx + 1) * sliceLength - 1`.
In `@packages/async/stories/makeAbortable.stories.tsx`:
- Around line 75-77: The input element in the makeAbortable.stories.tsx file is
using the onChange event handler, which in SolidJS only fires when the input
loses focus, causing a lag in the auto-suggest behavior. Replace the onChange
prop with onInput on the input element to ensure the setQuery callback fires on
every keystroke, providing real-time suggestions as the user types.
In `@packages/async/test/index.test.ts`:
- Line 168: Remove the debug console.log(result) statement from the retry test
in packages/async/test/index.test.ts. This console.log call serves no purpose in
the test and creates unnecessary noise in the CI/test output. Simply delete the
line containing console.log(result) from the test file.
In `@packages/resource/README.md`:
- Around line 11-12: The migration tip in the README.md file has unmatched
backtick markup that breaks markdown rendering. The text solid-js@>=2.0.0 has a
closing backtick but is missing the opening backtick, so it should be wrapped as
`solid-js@>=2.0.0` instead of solid-js@>=2.0.0` to properly render as inline
code.
---
Nitpick comments:
In `@packages/async/test/index.test.ts`:
- Around line 14-15: The helper streams in the test are not properly closing
when data is exhausted. In both locations where the code checks if done is true
and returns early (around the controller.enqueue calls), add a
controller.close() call before the return statement to ensure the stream
completion semantics are properly exercised. This applies to both instances
where this pattern appears in the file.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 2c4b2be6-35f2-4925-951b-e93fb948c6fe
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (16)
packages/async/LICENSEpackages/async/README.mdpackages/async/package.jsonpackages/async/src/index.tspackages/async/stories/createAbortable.stories.tsxpackages/async/stories/data.jsonpackages/async/stories/fromJSONStream.stories.tsxpackages/async/stories/fromStream.stories.tsxpackages/async/stories/makeAbortable.stories.tsxpackages/async/stories/makeAggregated.stories.tsxpackages/async/stories/makeRetrying.stories.tsxpackages/async/stories/tsconfig.jsonpackages/async/test/index.test.tspackages/async/test/server.test.tspackages/async/tsconfig.jsonpackages/resource/README.md
| "description": "A template primitive example.", | ||
| "author": "Your Name <you@youremail.com>", |
There was a problem hiding this comment.
Replace template metadata before publish.
description and author still look like scaffolding placeholders, which will leak to npm/package docs.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/async/package.json` around lines 4 - 5, In the package.json file,
update the placeholder metadata fields: replace the "description" field value
from "A template primitive example." with an accurate description of what the
async package does, and replace the "author" field value from "Your Name
<you@youremail.com>" with the actual author's name and email address. These
fields will be published to npm, so ensure they contain real, meaningful
information.
| "files": [ | ||
| "dist" | ||
| ], | ||
| "type": "module", | ||
| "module": "./dist/index.js", | ||
| "types": "./dist/index.d.ts", | ||
| "browser": {}, | ||
| "exports": { | ||
| "import": { | ||
| "@solid-primitives/source": "./src/index.ts", | ||
| "types": "./dist/index.d.ts", | ||
| "default": "./dist/index.js" | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Checking package manifests for `@solid-primitives/source` export vs published files..."
fd -p package.json packages | while read -r manifest; do
has_source_cond=$(jq -r '(.exports | tostring | contains("`@solid-primitives/source`")) // false' "$manifest")
if [ "$has_source_cond" = "true" ]; then
name=$(jq -r '.name // "<unknown>"' "$manifest")
files=$(jq -c '.files // []' "$manifest")
source_target=$(jq -r '
.exports as $e
| if ($e|type) == "object" then
($e.import["`@solid-primitives/source`"] // $e["."]?.import?["`@solid-primitives/source`"] // "<not-found>")
else "<not-found>" end
' "$manifest")
echo "$name | $manifest | source_target=$source_target | files=$files"
fi
done
echo
echo "Expected: either src is published (e.g., files includes \"src\") or source condition is removed/repointed for published artifact."Repository: solidjs-community/solid-primitives
Length of output: 10409
This pattern is systematic across the entire monorepo: the @solid-primitives/source export condition points to TypeScript sources (./src/index.ts), but all packages only publish the dist directory.
@solid-primitives/async (and 96 other packages) declare a source export for consumers to resolve directly to source files, but the files field restricts published artifacts to dist only. This means any tooling that enables the @solid-primitives/source condition will attempt to resolve a missing file from the published npm package.
Either publish the src directory (add "src" to files) or remove the source export condition for npm consumers.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/async/package.json` around lines 35 - 47, The exports field in
package.json declares a `@solid-primitives/source` export condition that points to
./src/index.ts, but the files array only includes the dist directory. This
creates a mismatch where consumers trying to use the `@solid-primitives/source`
export will fail because the src directory is not published to npm. Fix this by
choosing one of two approaches: either add "src" to the files array to publish
source files alongside dist, or remove the `@solid-primitives/source` export
condition from the exports field to prevent consumers from attempting to resolve
unpublished source files.
| - [`fromStream`](#fromStream) - wraps a fetch request to support web streams in memos or optimistic signals | ||
| - [`fromJSONStream`](#fromJSONStream) - wraps a fetch request returning a web stream containing (incomplete) JSON for the use in memos or optimistic signals | ||
| - [`makeAbortable`](#makeabortable) - sets up an AbortSignal with auto-abort on re-fetch or timeout | ||
| - [`createAbortable`](#createabortable) - like `makeAbortable`, but with automatic abort on cleanup | ||
| - [`makeRetrying`](#makeretrying) - wraps the fetcher to retry requests after a delay | ||
| - [`createAggregated`](#createAggregated) - aggregates the values of an accessor |
There was a problem hiding this comment.
Fix README anchor fragments in the primitive index.
These fragments won’t resolve with standard slugging (#fromstream, #fromjsonstream, #createaggregated), so the table links break.
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 13-13: Link fragments should be valid
Expected: #fromstream; Actual: #fromStream
(MD051, link-fragments)
[warning] 14-14: Link fragments should be valid
Expected: #fromjsonstream; Actual: #fromJSONStream
(MD051, link-fragments)
[warning] 18-18: Link fragments should be valid
Expected: #createaggregated; Actual: #createAggregated
(MD051, link-fragments)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/async/README.md` around lines 13 - 18, The anchor fragments in the
README table of contents use camelCase format (fromStream, fromJSONStream,
createAggregated, etc.) but standard markdown slug generation converts headings
to lowercase. Update all the anchor fragment links in the primitive index to use
lowercase versions (fromstream, fromjsonstream, makeabortable, createabortable,
makeretrying, createaggregated) so they correctly resolve to the actual heading
slugs in the document.
Source: Linters/SAST tools
| fromStream<Args extends any[], JSON extends any>( | ||
| webStreamOrResponse: (...args: Args) => ReadableStream | Response | ||
| ): (...args: Args) => AsyncGenerator<JSON, void, unknown>; |
There was a problem hiding this comment.
README API docs drift from implementation (fromJSONStream and abort options).
The fromJSONStream definition block is labeled fromStream, and docs/examples use noAutoAbort while the exported option is autoAbort (inverse semantics via autoAbort !== false in source). This will misconfigure consumers who copy the docs.
Also applies to: 108-109, 118-118
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/async/README.md` around lines 64 - 66, The README documentation
contains mismatches with the actual implementation for the fromJSONStream API.
First, correct the function label from "fromStream" to "fromJSONStream" in the
definition block that shows the actual function signature. Second, update all
documentation examples and option descriptions to use the correct option name
"autoAbort" instead of "noAutoAbort", ensuring the semantics are clearly
documented that autoAbort controls whether streams are automatically aborted
(with inverse logic to what the docs currently show). Apply these corrections to
all instances mentioned including the definition block around line 64-66 and the
additional locations at lines 108-109 and 118.
| const isIterable = (obj: unknown): obj is Iterable<unknown> => !!obj && Object.hasOwn(obj, Symbol.iterator); | ||
|
|
||
| const isAsyncIterable = (obj: unknown): obj is AsyncIterable<unknown> => !!obj && Object.hasOwn(obj, Symbol.asyncIterator); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Test Object.hasOwn behavior with Symbol.iterator on standard iterables
node <<'JS'
// Test arrays
console.log("Array:");
console.log(" Object.hasOwn([], Symbol.iterator):", Object.hasOwn([], Symbol.iterator));
console.log(" [Symbol.iterator] in []:", Symbol.iterator in []);
console.log(" typeof [][Symbol.iterator]:", typeof [][Symbol.iterator]);
// Test strings
console.log("\nString:");
console.log(" Object.hasOwn('', Symbol.iterator):", Object.hasOwn('', Symbol.iterator));
console.log(" Symbol.iterator in '':", Symbol.iterator in '');
console.log(" typeof ''[Symbol.iterator]:", typeof ''[Symbol.iterator]);
// Test generator
console.log("\nGenerator:");
const gen = (function*(){})();
console.log(" Object.hasOwn(gen, Symbol.iterator):", Object.hasOwn(gen, Symbol.iterator));
console.log(" Symbol.iterator in gen:", Symbol.iterator in gen);
console.log(" typeof gen[Symbol.iterator]:", typeof gen[Symbol.iterator]);
// Test async generator
console.log("\nAsync Generator:");
const asyncGen = (async function*(){})();
console.log(" Object.hasOwn(asyncGen, Symbol.asyncIterator):", Object.hasOwn(asyncGen, Symbol.asyncIterator));
console.log(" Symbol.asyncIterator in asyncGen:", Symbol.asyncIterator in asyncGen);
console.log(" typeof asyncGen[Symbol.asyncIterator]:", typeof asyncGen[Symbol.asyncIterator]);
JSRepository: solidjs-community/solid-primitives
Length of output: 992
🏁 Script executed:
# Find where isIterable and isAsyncIterable are used
cd packages/async/src && rg -n "isIterable|isAsyncIterable" --max-count=20Repository: solidjs-community/solid-primitives
Length of output: 450
🏁 Script executed:
# Check the full context around lines 171-173
cd packages/async/src && sed -n '165,230p' index.tsRepository: solidjs-community/solid-primitives
Length of output: 2578
Fix iterable detection; current guards reject standard iterables.
Object.hasOwn only checks own properties. Arrays, strings, and generator objects have Symbol.iterator on their prototype, so the current check fails for them. This causes makeRetrying (lines 206, 211) to mishandle iterables—an array returned by the fetcher will be treated as a single value instead of being iterated.
Proposed fix
-const isIterable = (obj: unknown): obj is Iterable<unknown> => !!obj && Object.hasOwn(obj, Symbol.iterator);
-
-const isAsyncIterable = (obj: unknown): obj is AsyncIterable<unknown> => !!obj && Object.hasOwn(obj, Symbol.asyncIterator);
+const isIterable = (obj: unknown): obj is Iterable<unknown> =>
+ !!obj && typeof (obj as { [Symbol.iterator]?: unknown })[Symbol.iterator] === "function";
+
+const isAsyncIterable = (obj: unknown): obj is AsyncIterable<unknown> =>
+ !!obj && typeof (obj as { [Symbol.asyncIterator]?: unknown })[Symbol.asyncIterator] === "function";📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const isIterable = (obj: unknown): obj is Iterable<unknown> => !!obj && Object.hasOwn(obj, Symbol.iterator); | |
| const isAsyncIterable = (obj: unknown): obj is AsyncIterable<unknown> => !!obj && Object.hasOwn(obj, Symbol.asyncIterator); | |
| const isIterable = (obj: unknown): obj is Iterable<unknown> => | |
| !!obj && typeof (obj as { [Symbol.iterator]?: unknown })[Symbol.iterator] === "function"; | |
| const isAsyncIterable = (obj: unknown): obj is AsyncIterable<unknown> => | |
| !!obj && typeof (obj as { [Symbol.asyncIterator]?: unknown })[Symbol.asyncIterator] === "function"; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/async/src/index.ts` around lines 171 - 173, The isIterable and
isAsyncIterable functions use Object.hasOwn to check for Symbol.iterator and
Symbol.asyncIterator, but this only detects own properties and misses symbols on
the prototype chain. Arrays, strings, and generators have these symbols
inherited from their prototypes, causing makeRetrying to incorrectly treat them
as single values. Replace Object.hasOwn with the in operator for both isIterable
and isAsyncIterable, since the in operator checks the entire prototype chain and
will correctly identify all standard iterables and async iterables.
| const sliceLength = Math.ceil(source.length / 16); | ||
| const parts = Array.from( | ||
| { length: packetCount }, | ||
| (_, idx) => source.slice(idx * sliceLength, (idx + 1) * sliceLength - 1) |
There was a problem hiding this comment.
JSON stream chunking has an off-by-one data loss bug.
Using slice(..., end - 1) removes one character per chunk, which corrupts the progressively parsed JSON.
Proposed fix
- (_, idx) => source.slice(idx * sliceLength, (idx + 1) * sliceLength - 1)
+ (_, idx) => source.slice(idx * sliceLength, (idx + 1) * sliceLength)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| (_, idx) => source.slice(idx * sliceLength, (idx + 1) * sliceLength - 1) | |
| (_, idx) => source.slice(idx * sliceLength, (idx + 1) * sliceLength) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/async/stories/fromJSONStream.stories.tsx` at line 33, The JSON
stream chunking in the anonymous function has an off-by-one error in the slice
end index calculation. The slice call currently subtracts 1 from the end index
with `(idx + 1) * sliceLength - 1`, which removes one character from each chunk
and corrupts the JSON parsing. Remove the `- 1` from the end index so that the
slice correctly includes all characters up to the proper boundary at `(idx + 1)
* sliceLength`.
| const sliceLength = Math.ceil(source.length / 16); | ||
| const parts = Array.from( | ||
| { length: packetCount }, | ||
| (_, idx) => source.slice(idx * sliceLength, (idx + 1) * sliceLength - 1) |
There was a problem hiding this comment.
Packet slicing drops characters at every chunk boundary.
slice(..., end - 1) is skipping one character per packet because slice end is already exclusive.
Proposed fix
- (_, idx) => source.slice(idx * sliceLength, (idx + 1) * sliceLength - 1)
+ (_, idx) => source.slice(idx * sliceLength, (idx + 1) * sliceLength)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| (_, idx) => source.slice(idx * sliceLength, (idx + 1) * sliceLength - 1) | |
| (_, idx) => source.slice(idx * sliceLength, (idx + 1) * sliceLength) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/async/stories/fromStream.stories.tsx` at line 33, The slice call in
the callback function (around the `source.slice` expression) is incorrectly
subtracting 1 from the end index parameter, which causes character loss at chunk
boundaries because JavaScript's slice method already treats the end index as
exclusive. Remove the `- 1` from the end parameter calculation in the slice call
so it becomes `(idx + 1) * sliceLength` instead of `(idx + 1) * sliceLength -
1`.
| <input | ||
| placeholder="type for autosuggest" | ||
| onChange={(ev) => { setQuery(ev.currentTarget.value)}} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
fd -t f "makeAbortable.stories.tsx"Repository: solidjs-community/solid-primitives
Length of output: 127
🏁 Script executed:
# Check if file exists and read the relevant section
if [ -f "packages/async/stories/makeAbortable.stories.tsx" ]; then
wc -l "packages/async/stories/makeAbortable.stories.tsx"
echo "---"
sed -n '70,80p' "packages/async/stories/makeAbortable.stories.tsx"
fiRepository: solidjs-community/solid-primitives
Length of output: 541
🌐 Web query:
onChange vs onInput event behavior in HTML text inputs modern browsers
💡 Result:
In modern browsers, the fundamental difference between the oninput and onchange events for HTML text inputs lies in when they are triggered relative to user interaction [1][2][3]. The oninput event fires synchronously and immediately every time the value of an element is modified [1][2][4]. This includes typing, pasting, dragging and dropping text, or using speech recognition [4]. Because it triggers on every alteration, it is the appropriate choice for features requiring real-time feedback, such as live character counts, instant form validation, or real-time search suggestions [4][5]. The onchange event, by contrast, does not fire for every single modification [1][3]. For text inputs, it fires only after the element has been changed and subsequently loses focus [1][4][3]. This makes it suitable for operations that should only occur after the user has finished their input and committed the change, such as performing a heavy calculation or saving data to a server [1][5]. Key distinctions to note: - Trigger Timing: oninput is immediate; onchange is typically deferred until focus is lost (for text inputs) [1][6][7]. - Programmatic Changes: Neither event fires when the element's value is changed programmatically via JavaScript [2]. - Framework Nuance: In some popular libraries like React, the onChange event is intentionally designed to behave like the native oninput event, firing on every keystroke, which can sometimes cause confusion for developers familiar only with vanilla JavaScript [5]. It is important to remember that for non-text elements (such as select, checkbox, or radio), the onchange event behaves differently—it often fires immediately upon selection or toggle, making the distinction between oninput and onchange less pronounced for those specific element types [1][3].
Citations:
- 1: https://stackoverflow.com/questions/17047497/difference-between-change-and-input-event-for-an-input-element
- 2: https://developer.mozilla.org/en/DOM/window.oninput
- 3: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event
- 4: https://javascript.info/events-change-input
- 5: https://digitalthriveai.com/en-us/resources/docs/web-development/onchanged/
- 6: https://www.w3schools.com/jsref/event_oninput.asp
- 7: https://www.w3schools.com/jsref/event_onchange.asp
🌐 Web query:
SolidJS onChange vs onInput event handlers behavior text input
💡 Result:
In SolidJS, the onInput and onChange event handlers for text input elements behave exactly according to their native browser DOM counterparts [1][2]. onInput The onInput event fires immediately every time the value of an input element changes [3][4][2]. This is the appropriate choice for scenarios where you need real-time synchronization between the input element and your application state (e.g., updating a signal on every keystroke) [1][3]. onChange The onChange event fires only when the element loses focus (blurs) after its value has been modified [1][3][5]. For text-based inputs, it does not trigger on every keystroke [5]. Key differences summary: - onInput: Fires immediately on every character change [3][4][2]. - onChange: Fires only after the input loses focus [3][4][2]. Both handlers can be used with SolidJS's event delegation (e.g., onInput={...}) or native event binding (e.g., on:input={...}) depending on your requirements for performance versus event control [4][2].
Citations:
- 1: https://typeofnan.dev/how-to-use-onchange-in-solidjs/
- 2: https://docs.solidjs.com/concepts/components/event-handlers
- 3: https://www.tutorialspoint.com/solidjs/solidjs-user-input-handling.htm
- 4: https://www.tutorialspoint.com/solidjs/solidjs-event-handling.htm
- 5: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event
🏁 Script executed:
# Check SolidJS version and documentation references
if [ -f "package.json" ]; then
grep -A 5 '"solid' package.json | head -20
fi
# Look for any SolidJS event handler documentation or examples in the repo
rg "onInput|onChange" packages/async/stories/ -A 2 -B 2 --max-count=5Repository: solidjs-community/solid-primitives
Length of output: 1287
Use onInput for real-time auto-suggest behavior.
In SolidJS, onChange on text inputs fires only when the element loses focus, causing suggestions to lag. For "type-to-suggest" functionality, use onInput which fires on every keystroke.
Proposed fix
<input
placeholder="type for autosuggest"
- onChange={(ev) => { setQuery(ev.currentTarget.value)}}
+ onInput={(ev) => setQuery(ev.currentTarget.value)}
/>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <input | |
| placeholder="type for autosuggest" | |
| onChange={(ev) => { setQuery(ev.currentTarget.value)}} | |
| <input | |
| placeholder="type for autosuggest" | |
| onInput={(ev) => setQuery(ev.currentTarget.value)} | |
| /> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/async/stories/makeAbortable.stories.tsx` around lines 75 - 77, The
input element in the makeAbortable.stories.tsx file is using the onChange event
handler, which in SolidJS only fires when the input loses focus, causing a lag
in the auto-suggest behavior. Replace the onChange prop with onInput on the
input element to ensure the setQuery callback fires on every keystroke,
providing real-time suggestions as the user types.
| const fetcher = (_prev: unknown) => responses.shift() || Promise.resolve(true); | ||
| const wrapped = makeRetrying(fetcher, { delay: 15 }); | ||
| const result = wrapped()[Symbol.asyncIterator]().next(); | ||
| console.log(result) |
There was a problem hiding this comment.
Remove debug logging from the retry test.
console.log(result) is leftover debug noise in CI/test output.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/async/test/index.test.ts` at line 168, Remove the debug
console.log(result) statement from the retry test in
packages/async/test/index.test.ts. This console.log call serves no purpose in
the test and creates unnecessary noise in the CI/test output. Simply delete the
line containing console.log(result) from the test file.
| > [!TIP] | ||
| > solid-js@>=2.0.0` no longer uses resources. You can find most of these helpers for the new version in the `@solid-primitives/async` package. |
There was a problem hiding this comment.
Fix malformed inline-code markup in migration tip.
The version spec has an unmatched backtick, so markdown renders incorrectly.
Suggested patch
-> solid-js@>=2.0.0` no longer uses resources. You can find most of these helpers for the new version in the `@solid-primitives/async` package.
+> `solid-js@>=2.0.0` no longer uses resources. You can find most of these helpers for the new version in the `@solid-primitives/async` package.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| > [!TIP] | |
| > solid-js@>=2.0.0` no longer uses resources. You can find most of these helpers for the new version in the `@solid-primitives/async` package. | |
| > [!TIP] | |
| > `solid-js@>=2.0.0` no longer uses resources. You can find most of these helpers for the new version in the `@solid-primitives/async` package. |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/resource/README.md` around lines 11 - 12, The migration tip in the
README.md file has unmatched backtick markup that breaks markdown rendering. The
text solid-js@>=2.0.0 has a closing backtick but is missing the opening
backtick, so it should be wrapped as `solid-js@>=2.0.0` instead of
solid-js@>=2.0.0` to properly render as inline code.
This should replace the resource package, featuring the following primitives:
fromStream/fromJSONStream: (small helper to aggregate web streams); maybe in the future, we can also support fromJSONpStream for progressive JSON or similar formatsmakeAbortable/createAbortable: the same as in the resource package, but chainable for actionsmakeRetryingmakeCache(-> will go into the fetch package)Summary by CodeRabbit
New Features
@solid-primitives/asyncpackage featuring stream utilities for incremental data processing, abort/signal management with timeout support, automatic retry mechanisms with configurable delays, and reactive value aggregation capabilities.Documentation